/**************************************************************************************************
  Filename:       ota_service.c
  Revised:         
  Revision:       

  Description:    This file contains the Simple GATT profile sample GATT service 
                  profile for use with the BLE sample application.

 
**************************************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "sdk_config.h"
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "ll_def.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "peripheral.h"
#include "gattservapp.h"
#include "gapbondmgr.h"

#include "ota_service.h"
#include "log.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */


/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// OTA GATT Profile Service UUID: 0xFE59
CONST uint8 otaProfileServUUID[ATT_UUID_SIZE] =
{ 
	CS_OTA_BASE_UUID_128(OTAPROFILE_SERV_UUID)
};

// OTA Control Point: 0x8EC90001-F315-4F60-9FB8-838830DAEA50
CONST uint8 otaProfileCtlPointUUID[ATT_UUID_SIZE] =
{ 
	CS_OTA_BASE_UUID_128(OTAPROFILE_CTL_POINT_CHAR_UUID)
};

// OTA Packet: 0x8EC90002-F315-4F60-9FB8-838830DAEA50
CONST uint8 otaProfilePacketUUID[ATT_UUID_SIZE] =
{ 
	CS_OTA_BASE_UUID_128(OTAPROFILE_PACKET_CHAR_UUID)
};

// OTA Revision: 0x8EC90002-F315-4F60-9FB8-838830DAEA50
CONST uint8 otaProfileRevisionUUID[ATT_UUID_SIZE] =
{ 
	CS_OTA_BASE_UUID_128(OTAPROFILE_REV_CHAR_UUID)
};

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static otaProfileCBs_t *otaProfile_AppCBs = NULL;

/*********************************************************************
 * Profile Attributes - variables
 */

// OTA Profile Service attribute
static CONST gattAttrType_t otaProfileService = { ATT_UUID_SIZE, otaProfileServUUID };


// OTA Profile OTA Control Point Properties
static uint8 otaProfileCtlPointProps = GATT_PROP_WRITE | GATT_PROP_NOTIFY;

// OTA Profile OTA Control Point Value
static uint8 otaProfileCtlPointValue[OTAPROFILE_CTL_POINT_CHAR_LEN];

// OTA Profile OTA Control Point client configuration
static gattCharCfg_t otaProfileCtlPointConfig[GATT_MAX_NUM_CONN];

// OTA Profile OTA Packet Properties
static uint8 otaProfilePacketProps = GATT_PROP_WRITE_NO_RSP;

// OTA Profile OTA Packet Value
static uint8 otaProfilePacketValue[OTAPROFILE_PACKET_CHAR_LEN];

// OTA Profile OTA Revision Properties
static uint8 otaProfileRevisionProps = GATT_PROP_READ;

// OTA Profile OTA Revision Value
static uint8 otaProfileRevisionValue[2] = {0x00, 0x01};

static uint8 ota_dev_connHandle = NULL;
/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t otaProfileAttrTbl[] = 
{
  // OTA Profile Service
  { 
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&otaProfileService            /* pValue */
  },

    // OTA Control Point Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &otaProfileCtlPointProps 
    },

      // OTA Control Point Value
      { 
        { ATT_UUID_SIZE, otaProfileCtlPointUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        otaProfileCtlPointValue 
      },
	  
	  // OTA Control Point configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *)&otaProfileCtlPointConfig 
      },  

    // OTA Packet Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &otaProfilePacketProps 
    },

      // OTA Packet Value
      { 
        { ATT_UUID_SIZE, otaProfilePacketUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        otaProfilePacketValue 
      },     
	  
	 // OTA Revision Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &otaProfileRevisionProps 
    },

      // OTA Revision Value
      { 
        { ATT_UUID_SIZE, otaProfileRevisionUUID },
        GATT_PERMIT_READ, 
        0, 
        otaProfileRevisionValue 
      },     
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t otaProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint16 len, uint16 offset );
static bStatus_t otaProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                            uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen );
static void otaProfile_ConnStatusCB( uint16 connHandle, uint8 changeType );

/*********************************************************************
 * PROFILE CALLBACKS
 */
// OTA Profile Service Callbacks
CONST gattServiceCBs_t otaProfileCBs =
{
  otaProfile_ReadAttrCB,  // Read callback function pointer
  otaProfile_WriteAttrCB, // Write callback function pointer
  NULL                       // Authorization callback function pointer
};

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      OTAProfile_AddService
 *
 * @brief   Initializes the OTA Profile service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
bStatus_t OTAProfile_AddService( uint32 services )
{
	uint8 status = SUCCESS;
	
	// Register with Link DB to receive link status change callback
	VOID linkDB_Register( otaProfile_ConnStatusCB  );  

	// Initialize Client Characteristic Configuration attributes
	GATTServApp_InitCharCfg( INVALID_CONNHANDLE, otaProfileCtlPointConfig );
	//GATTServApp_InitCharCfg( INVALID_CONNHANDLE, otaProfilePacketConfig );
  
	if ( services & OTAPROFILE_SERVICE )
	{
		// Register GATT attribute list and CBs with GATT Server App
		status = GATTServApp_RegisterService( otaProfileAttrTbl, 
                                          GATT_NUM_ATTRS( otaProfileAttrTbl ),
                                          &otaProfileCBs );
	}

	return ( status );
}


/*********************************************************************
 * @fn      OTAProfile_RegisterAppCBs
 *
 * @brief   Registers the application callback function. Only call 
 *          this function once.
 *
 * @param   callbacks - pointer to application callbacks.
 *
 * @return  SUCCESS or bleAlreadyInRequestedMode
 */
bStatus_t OTAProfile_RegisterAppCBs( otaProfileCBs_t *appCallbacks )
{
  if ( appCallbacks )
  {
    otaProfile_AppCBs = appCallbacks;
    
    return ( SUCCESS );
  }
  else
  {
    return ( bleAlreadyInRequestedMode );
  }
}
  

/*********************************************************************
 * @fn      OTAProfile_SetParameter
 *
 * @brief   Set a OTA Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   value - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t OTAProfile_SetParameter( uint8 param, uint8 len, void *value )
{
	bStatus_t ret = SUCCESS;
	switch ( param )
	{
		default:
			ret = INVALIDPARAMETER;
			break;
	}
  
	return ( ret );
}

/*********************************************************************
 * @fn      OTAProfile_GetParameter
 *
 * @brief   Get a OTA Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   value - pointer to data to put.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t OTAProfile_GetParameter( uint8 param, void *value )
{
	bStatus_t ret = SUCCESS;
	switch ( param )
	{
		default:
			ret = INVALIDPARAMETER;
		break;
	}
  
	return ( ret );
}

bStatus_t OTAProfile_Notify(attHandleValueNoti_t *pNotify)
{
	uint16 connHandle;
	uint16 value;

	//GAPMultiRole_GetParameter(GAPROLE_CONNHANDLE, &connHandle);
	if(ota_dev_connHandle == INVALID_CONNHANDLE)
		return bleIncorrectMode;
  
	value = GATTServApp_ReadCharCfg( ota_dev_connHandle, otaProfileCtlPointConfig);
	// If notifications enabled
	if ( value & GATT_CLIENT_CFG_NOTIFY )
	{
		// Set the handle
		pNotify->handle = otaProfileAttrTbl[OTAPROFILE_CTL_POINT_HANDLE].handle;
		// Send the Indication
		return GATT_Notification( ota_dev_connHandle, pNotify, FALSE);
	}
	else {
		LOG("Notification is not enable\r\n");
	}
	return bleIncorrectMode;
  
}

static bStatus_t otaProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                            uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen )
{
	bStatus_t status = SUCCESS;

  // If attribute permissions require authorization to read, return error
  if ( gattPermitAuthorRead( pAttr->permissions ) )
  {
    // Insufficient authorization
    return ( ATT_ERR_INSUFFICIENT_AUTHOR );
  }
  
  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }
 
  if ( pAttr->type.len == ATT_BT_UUID_SIZE )
  {
    // 16-bit UUID
    uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    switch ( uuid )
    {
      default:
	      // Should never get here! (characteristics 3 and 4 do not have read permissions)
	      *pLen = 0;
	      status = ATT_ERR_ATTR_NOT_FOUND;
	      break;
    }
  }
  else
  {
    // 128-bit UUID
	  if(pAttr->type.uuid == otaProfileRevisionUUID) {
		*pLen = 2;
        VOID osal_memcpy( pValue, pAttr->pValue, 2 );
	  }
	  else {
		*pLen = 0;
	     status = ATT_ERR_ATTR_NOT_FOUND;
	  }
  }

  return ( status );
}

/*********************************************************************
 * @fn      otaProfile_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
 // TODO: test this function
static bStatus_t otaProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint16 len, uint16 offset )
{
	bStatus_t status = SUCCESS;
	OtaEvt_t evt = {
		.type = OTA_EVT_NULL,
		.len = 0
	};
  
	// If attribute permissions require authorization to write, return error
	if ( gattPermitAuthorWrite( pAttr->permissions ) )
	{
		// Insufficient authorization
		return ( ATT_ERR_INSUFFICIENT_AUTHOR );
	}
	if(pAttr->type.len == ATT_BT_UUID_SIZE) 
	{
		uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
		switch ( uuid )
		{
			case GATT_CLIENT_CHAR_CFG_UUID:
				status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                 offset, GATT_CLIENT_CFG_NOTIFY );
				if(status == SUCCESS && pAttr->handle == otaProfileAttrTbl[OTAPROFILE_CTL_POINT_CFG_HANDLE].handle) {
					uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );
					evt.type = (charCfg == 0) ? OTA_EVT_NOTIF_DISABLE: OTA_EVT_NOTIF_ENABLE;
					LOG("Enable Notification\r\n");
				}
			break;
        
			default:
				// Should never get here! (characteristics 2 and 4 do not have write permissions)
				status = ATT_ERR_ATTR_NOT_FOUND;
			break;
		}
	}
	else
	{
		// 128-bit UUID
    ota_dev_connHandle = connHandle;
		if(pAttr->type.uuid == otaProfileCtlPointUUID || pAttr->type.uuid == otaProfilePacketUUID) {
			//Write the value
			if ( status == SUCCESS )
			{
				if( pAttr->pValue == otaProfileCtlPointValue )
				{
					evt.type = OTA_EVT_CONTROL;   
					evt.len = len;
					evt.data = pValue;
				}
				else
				{
					evt.type = OTA_EVT_DATA; 
					evt.len = len;
					evt.data = pValue;					
				}
			}
		}
	}
  
  // If a charactersitic value changed then callback function to notify application of change
  if (evt.type != OTA_EVT_NULL && otaProfile_AppCBs && otaProfile_AppCBs->pfnOTAProfileChange )
  {
    otaProfile_AppCBs->pfnOTAProfileChange( &evt );  
  }
  
  return ( status );
}


static void otaProfile_ConnStatusCB( uint16 connHandle, uint8 changeType )
{ 
	OtaEvt_t evt = {
		.type = OTA_EVT_NULL,
		.len = 0
	};
  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    // Reset Client Char Config if connection has dropped
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
           ( !linkDB_Up( connHandle ) ) ) )
    { 
      GATTServApp_InitCharCfg( connHandle, otaProfileCtlPointConfig);
	  //disconnect
	  evt.type = OTA_EVT_DISCONNECTED;
    }
	else if(( changeType == LINKDB_STATUS_UPDATE_NEW )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
           ( linkDB_Up( connHandle ) ) ) ) 
	{
	  //connect
	  evt.type = OTA_EVT_CONNECTED;
	}
  }
  if (evt.type != OTA_EVT_NULL && otaProfile_AppCBs && otaProfile_AppCBs->pfnOTAProfileChange )
  {
    otaProfile_AppCBs->pfnOTAProfileChange( &evt );  
  }
}

/*********************************************************************
*********************************************************************/
